
#include <gcc-plugin.h>

#include <langhooks.h>
#include <tree.h>
#include <print-tree.h>
#include <tree-pass.h>
#include <gimple.h>
#include <gimple-pretty-print.h>
#include <gimple-iterator.h>
#include <cgraph.h>
#include <context.h>
#include <stringpool.h>
#include <target.h>
#include <hooks.h>
#include <rtl.h>
#include <genrtl.h>
#include <expr.h>
#include <memmodel.h>
#include <emit-rtl.h>
#include <attribs.h>
#include <string.h>
#include <set>
// #include "gfortran.h"
#include <diagnostic-core.h>
#include <gimple-walk.h>
#include <gimple-ssa.h>
static attribute_spec attribute_sw_64_host = {"sw_64_host", 0,     0,    false,
                                              false,        false, NULL, false};
static attribute_spec attribute_sw_64_slave = {
    "sw_64_slave", 0, 0, false, false, false, NULL, false};

void initialize_attributes(void *, void *) {
  register_attribute(&attribute_sw_64_host);
  register_attribute(&attribute_sw_64_slave);
}

namespace {
pass_data pass_data_omp2ath = {GIMPLE_PASS,
                               "omp2ath",
                               OPTGROUP_OMP,
                               TV_NONE,
                               PROP_gimple_any,
                               PROP_gimple_eomp,
                               0,
                               0,
                               0};
pass_data pass_data_omp2ath_fold_acc = {GIMPLE_PASS,
                                        "omp2ath_fold_acc",
                                        OPTGROUP_OMP,
                                        TV_NONE,
                                        PROP_gimple_any,
                                        PROP_gimple_eomp,
                                        0,
                                        0,
                                        0};
pass_data pass_data_omp2ath_lower_nested = {GIMPLE_PASS,
                                            "omp2ath_lower_nested",
                                            OPTGROUP_OMP,
                                            TV_NONE,
                                            PROP_gimple_any,
                                            PROP_gimple_eomp,
                                            0,
                                            0,
                                            0};
pass_data pass_data_omp2ath_filter = {SIMPLE_IPA_PASS,
                                      "omp2ath_filter",
                                      OPTGROUP_NONE,
                                      TV_NONE,
                                      PROP_cfg | PROP_cfglayout,
                                      0,
                                      0,
                                      0,
                                      0};
pass_data pass_data_slave_trace_calls = {
    RTL_PASS, "tracing_calls", OPTGROUP_NONE, TV_NONE, PROP_rtl, 0, 0};
}; // namespace
int omp2ath_dim_limit(int axis) {
  //   printf("axis: %d\n", axis);
  if (axis == 0)
    return 64;
  else
    return 0;
}
class omp2ath_filter : public simple_ipa_opt_pass {
public:
  omp2ath_filter(gcc::context *ctx)
      : simple_ipa_opt_pass(pass_data_omp2ath_filter, ctx) {}
  static void mark_declare_target_children(cgraph_node *node) {
    for (cgraph_edge *edge = node->callees; edge; edge = edge->next_callee) {
      auto callee_decl = edge->callee->decl;
      if (edge->callee->definition && callee_decl &&
          !lookup_attribute("omp declare target",
                            DECL_ATTRIBUTES(callee_decl))) {
        DECL_ATTRIBUTES(callee_decl) = make_attribute(
            "omp declare target", "", DECL_ATTRIBUTES(callee_decl));
        fprintf(stderr, "%s is considered for slave code generation\n",
                IDENTIFIER_POINTER(DECL_ASSEMBLER_NAME(callee_decl)));
        mark_declare_target_children(edge->callee);
      }
    }
  }
  int unsigned execute(function *) override {
    bool has_slave = false;
    auto cccmp = [](const char *a, const char *b) { return strcmp(a, b) < 0; };
    std::set<const char *, decltype(cccmp)> entries(cccmp);
    if (TARGET_SW_SLAVE) {
      if (getenv("O2ATH_ENTRIES")) {
        const char *entry_str = xstrdup(getenv("O2ATH_ENTRIES"));
        int start = 0, iend;
        for (iend = 0; entry_str[iend]; iend++) {
          if (entry_str[iend] == ':') {
            const char *fname = xstrndup(entry_str + start, iend - start);
            entries.insert(fname);
            start = iend + 1;
          }
        }
        if (start != iend)
          entries.insert(xstrndup(entry_str + start, iend - start));
      }
      for (auto s : entries) {
        fprintf(stderr,
                "%s is considered for generating slave code due to explicit "
                "setup\n",
                s);
      }
    }
    cgraph_node *node;
    FOR_EACH_FUNCTION(node) {
      auto decl = node->decl;
      const char *fname = IDENTIFIER_POINTER(DECL_ASSEMBLER_NAME(decl));
      bool target_entry_p =
          DECL_ATTRIBUTES(decl) &&
          lookup_attribute("omp target entrypoint", DECL_ATTRIBUTES(decl));
      bool target_p =
          DECL_ATTRIBUTES(decl) &&
          lookup_attribute("omp declare target", DECL_ATTRIBUTES(decl));
      if (target_p || target_entry_p)
        has_slave = true;
      if (TARGET_SW_HOST) {
        if (target_entry_p) {
          DECL_EXTERNAL(decl) = 1;
          TREE_PUBLIC(decl) = 1;
          node->externally_visible = true;
          SET_DECL_ASSEMBLER_NAME(
              node->decl, get_identifier(ACONCAT(("slave_", fname, NULL))));
        }
        if (lookup_attribute("onlyslave", DECL_ATTRIBUTES(decl))) {
          DECL_EXTERNAL(decl) = 1;
          node->force_output = false;
        }
      }
      if (TARGET_SW_SLAVE && (target_p || target_entry_p) &&
          !getenv("NORECURSIVE")) {
        mark_declare_target_children(node);
      }
    }
    if (TARGET_SW_SLAVE) {
      FOR_EACH_FUNCTION(node) {
        auto decl = node->decl;
        const char *fname = IDENTIFIER_POINTER(DECL_ASSEMBLER_NAME(decl));
        if (entries.find(fname) != entries.end()) {
          DECL_ATTRIBUTES(decl) =
              make_attribute("omp declare target", "", DECL_ATTRIBUTES(decl));
        }
        bool entry =
            lookup_attribute("omp target entrypoint", DECL_ATTRIBUTES(decl));
        bool needed = entry || lookup_attribute("omp declare target",
                                                DECL_ATTRIBUTES(decl));
        if (entry) {
          TREE_PUBLIC(decl) = 1;
          node->externally_visible = true;
        }
        if (needed) {
          // node->mark_force_output();
          if (DECL_STATIC_CHAIN(decl)) {
            fprintf(stderr,
                    "%s is considered as always_inline to avoid static-chain "
                    "calls\n",
                    fname);
            DECL_DECLARED_INLINE_P(decl) = 1;
            DECL_ATTRIBUTES(decl) = make_attribute(
                "no_instrument_function", "",
                make_attribute("always_inline", "", DECL_ATTRIBUTES(decl)));
            DECL_DISREGARD_INLINE_LIMITS(decl) = 1;
          }
          node->externally_visible = true;
          node->parallelized_function = 0;
          node->resolution = LDPR_RESOLVED_IR;
          // puts(fname);
        } else {
          DECL_EXTERNAL(decl) = 1;
        }
      }
    }
    if (TARGET_SW_HOST && has_slave && getenv("SLAVE_FLAG")) {
      fclose(fopen(getenv("SLAVE_FLAG"), "w"));
    }
    return 0;
  }
};
class omp2ath : public gimple_opt_pass {
public:
  bool initialized;
  tree spawn_fn_acc;
  tree spawn_fn_omp;
  omp2ath(gcc::context *ctx) : gimple_opt_pass(pass_data_omp2ath, ctx) {
    initialized = false;
    spawn_fn_acc = NULL_TREE;
    spawn_fn_omp = NULL_TREE;
  }

  tree get_spawn_acc() {
    if (spawn_fn_acc)
      return spawn_fn_acc;
    auto ftype_acc =
        build_function_type_list(void_type_node, intSI_type_node, ptr_type_node,
                                 intSI_type_node, ptr_type_node, ptr_type_node,
                                 ptr_type_node, intSI_type_node, NULL_TREE);
    spawn_fn_acc = build_fn_decl("acc2ath_parallel_keyed", ftype_acc);

    cgraph_node::get_create(spawn_fn_acc);
    return spawn_fn_acc;
  }

  tree get_spawn_omp() {
    if (spawn_fn_omp)
      return spawn_fn_omp;
    auto ftype_omp = build_function_type_list(
        void_type_node, intSI_type_node, ptr_type_node, intSI_type_node,
        ptr_type_node, ptr_type_node, ptr_type_node, intSI_type_node,
        intDI_type_node, ptr_type_node, NULL_TREE);
    spawn_fn_omp = build_fn_decl("omp2ath_target_ext", ftype_omp);
    cgraph_node::get_create(spawn_fn_omp);
    return spawn_fn_omp;
  }

  int unsigned execute(function *f) override {
    basic_block bb;
    const char *fname = IDENTIFIER_POINTER(DECL_ASSEMBLER_NAME(f->decl));
    auto node = cgraph_node::get(f->decl);

    if (TARGET_SW_HOST) {
      FOR_EACH_BB_FN(bb, f) {
        for (auto gsi = gsi_start_bb(bb); !gsi_end_p(gsi); gsi_next(&gsi)) {
          auto stmt = gsi_stmt(gsi);

          if (stmt && gimple_code(stmt) == GIMPLE_CALL) {
            auto fn = gimple_call_fn(stmt);
            if (fn && TREE_CODE(fn) == ADDR_EXPR) {
              auto fncalled = TREE_OPERAND(gimple_call_fn(stmt), 0);
              if (!strcmp(IDENTIFIER_POINTER(DECL_NAME(fncalled)),
                          "__builtin_GOACC_parallel_keyed")) {
                TREE_OPERAND(gimple_call_fn(stmt), 0) = get_spawn_acc();
              }
              if (!strcmp(IDENTIFIER_POINTER(DECL_NAME(fncalled)),
                          "__builtin_GOMP_target_ext")) {
                TREE_OPERAND(gimple_call_fn(stmt), 0) = get_spawn_omp();
              }
            }
          }
        }
      }
    }
    if (TARGET_SW_SLAVE) {
      varpool_node *node;
      // FOR_EACH_DEFINED_VARIABLE(node) {
      //   if (node->decl){
      //     // puts(IDENTIFIER_POINTER(DECL_NAME(node->decl)));
      //     if (!strncmp(IDENTIFIER_POINTER(DECL_NAME(node->decl)), "__vtab_",
      //     7)) {
      //       debug_tree(node->decl);
      //     }
      //   }
      // }
      // if (!strcmp(fname, "__cam_map_utils_MOD_cam_filemap_get_blocksize")) {
      FOR_EACH_BB_FN(bb, f) {
        for (auto gsi = gsi_start_bb(bb); !gsi_end_p(gsi); gsi_next(&gsi)) {
          auto stmt = gsi_stmt(gsi);
          if (stmt) {
            if (gimple_code(stmt) == GIMPLE_ASSIGN) {
              // if (gimple_assign_rhs_code(stmt) == ADDR_EXPR) {
              //   printf("%s\n", gimple_code_name[gimple_code(stmt)]);
              //   // printf("rhscode: %s\n",
              //   debug_tree(gimple_assign_rhs1(stmt));
              //   debug_tree(build_fold_addr_expr_loc(gimple_location(stmt),
              //   TREE_OPERAND(gimple_assign_rhs1(stmt), 0)));
              //   // get_tree_code_name(TREE_CODE(gimple_assign_rhs1(stmt))));
              // }
              if (gimple_assign_rhs_code(stmt) == COMPONENT_REF) {
                auto rhs = gimple_assign_rhs1(stmt);
                // debug_tree (rhs);
                auto field = TREE_OPERAND(rhs, 1);
                auto object = TREE_OPERAND(rhs, 0);
                // if (TREE_CODE(object) == MEM_REF)
                //   object = TREE_OPERAND(object, 0);
                //__vtab_cam_map_utils_Cam_filemap_t
                //__vtype_cam_map_utils_Cam_filemap_t
                // debug_tree(field);
                // debug_tree(object);
                if (DECL_NAME(field) &&
                    !strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "_vptr")) {
                  // debug_tree(TREE_TYPE(object));
                  // auto dat = TYPE_FIELDS(TREE_TYPE(object));
                  // auto vptr = TREE_CHAIN(TYPE_FIELDS(TREE_TYPE(object)));
                  // debug_tree(vptr);
                  // debug_tree(TREE_TYPE(field));
                  // debug_tree(TREE_TYPE(TREE_TYPE(field)));
                  auto vtype = TREE_TYPE(TREE_TYPE(field));
                  // debug_tree(vtype);
                  // printf("%p\n", DECL_CONTEXT(object));
                  // debug_tree(TYPE_CONTEXT(vtype));
                  // debug_tree(DECL_ASSEMBLER_NAME(TYPE_CONTEXT(vtype)));
                  // debug_tree(DECL_NAME(TYPE_CONTEXT(vtype)));
                  const char *name = IDENTIFIER_POINTER(TYPE_NAME(vtype));
                  const char *ns;
                  if (TYPE_CONTEXT(vtype)) {
                    ns = ACONCAT(
                        ("__",
                         IDENTIFIER_POINTER(DECL_NAME(TYPE_CONTEXT(vtype))),
                         "_MOD_", NULL));
                  } else {
                    ns = "";
                  }
                  // printf("%p\n",
                  // varpool_node::get_for_asmname(get_identifier("__cam_map_utils_MOD___vtab_cam_map_utils_Cam_filemap_t")));
                  const char *vtab_asm_name =
                      ACONCAT((ns, "__vtab", name + 7, NULL));
                  puts(vtab_asm_name);
                  varpool_node *vn = varpool_node::get_for_asmname(
                      get_identifier(vtab_asm_name));
                  if (!vn) {
                    auto vtab_decl =
                        build_decl(UNKNOWN_LOCATION, VAR_DECL,
                                   get_identifier(vtab_asm_name), vtype);
                    SET_DECL_ASSEMBLER_NAME(vtab_decl,
                                            get_identifier(vtab_asm_name));
                    vn = varpool_node::get_create(vtab_decl);
                  }
                  debug_tree(TYPE_CONTEXT(vtype));
                  printf("%p\n", vn);
                  // vn->dump(stdout);
                  gimple_assign_set_rhs_code(stmt, ADDR_EXPR);
                  gimple_assign_set_rhs1(
                      stmt, build_fold_addr_expr_loc(gimple_location(stmt),
                                                     vn->decl));
                  warning_at(gimple_location(stmt), 0,
                             "devirtualized _vptr for %s::%s to %s", name, ns,
                             vtab_asm_name);
                  // debug(stmt);
                  // printf("%p\n", );
                  // auto vtype_hash = TYPE_FIELDS(vtype);
                  // debug_tree(vtype_hash);
                  // auto vtype_size = TREE_CHAIN(vtype_hash);
                  // auto vtype_extends = TREE_CHAIN(vtype_size);
                  // auto vtype_definit = TREE_CHAIN(vtype_extends);
                  // debug_tree(vtype_extends);
                  // debug_tree(TREE_CHAIN(TREE_CHAIN(vtype_definit)));
                  // gimple_assign_set_rhs_code(stmt, ADDR_EXPR);
                  // gimple_assign_set_rhs1(stmt, vtype_definit);
                  // auto fields = TYPE_FIELDS(TREE_TYPE(TREE_TYPE(field)));
                  // for (auto field = fields; field; field =
                  // TREE_CHAIN(field)) {
                  //   debug_tree(field);
                  //   debug_tree(DECL_INITIAL(field));
                  // }
                }
              }
              // debug(stmt);
            }
            // }
          }
        }
      }
    }
    // if (TARGET_SW_SLAVE) {
    //   FOR_EACH_BB_FN(bb, f) {
    //     for (auto gsi = gsi_start_bb(bb); !gsi_end_p(gsi); gsi_next(&gsi)) {
    //       auto stmt = gsi_stmt(gsi);
    //       if (stmt && gimple_call_internal_p(stmt)) {
    //         //   debug(stmt);

    //         if (gimple_call_internal_fn(stmt) == IFN_GOACC_TILE) {
    //           gassign *assign = gimple_build_assign(gimple_call_lhs(stmt),
    //                                                 gimple_call_arg(stmt,
    //                                                 2));
    //           gsi_replace(&gsi, assign, true);
    //         }
    //       }
    //     }
    //   }
    // }
    // puts("");
    return 0;
  }
};

class omp2ath_lower_nested : public gimple_opt_pass {
public:
  omp2ath_lower_nested(gcc::context *ctx)
      : gimple_opt_pass(pass_data_omp2ath_lower_nested, ctx) {}
  bool gate(function *f) override { return TARGET_SW_SLAVE; }
  static tree walk_op(tree *node, int *, void *) {
    debug_tree(*node);
    return NULL_TREE;
  }
  static tree walk_gop(gimple_stmt_iterator *, bool *,
                       struct walk_stmt_info *) {
    return nullptr;
  }
  int unsigned execute(function *f) override {

    puts("this is a very early pass");
    debug_tree(f->decl);
    // walk_tree(&(f->decl), walk_op, NULL, NULL);
    // walk_func
    walk_stmt_info wi;
    // printf("%p\n", f->static_chain_decl);
    // debug_tree(f->static_chain_decl);
    if (f->static_chain_decl) {
      DECL_ATTRIBUTES(f->decl) =
          make_attribute("always_inline", "", DECL_ATTRIBUTES(f->decl));
      // DECL_COMDAT(f->decl) = false;
      // TREE_PUBLIC(f->decl) = 0;
      // printf("%d\n", targetm.binds_local_p(f->decl));
    }
    // for (auto t: *(f->local_decls)){
    //   debug_tree(t);
    // }
    gimple_df df;
    debug_gimple_seq(f->gimple_body);
    // walk_gimple_seq(f->gimple_body, walk_gop, walk_op, &wi);
    return 0;
  }
};
// bool omp2ath_validate_dims(tree decl, int *dims, int fn_lev) {
//   // debug_tree(decl);
//   if (!decl) {
//     // printf("lev: %d\n", fn_lev);
//     dims[0] = 64;
//     dims[1] = -1;
//     dims[2] = -1;
//     return true;
//   }
//   return false;
// }
// rtx_insn *omp2ath_dim_pos(rtx target, rtx dim) {
//   //   debug(target);
//   //   debug(dim);
//   auto sref =
//       gen_rtx_SYMBOL_REF(DImode,
//       IDENTIFIER_POINTER(get_identifier("_MYID")));
//   SYMBOL_REF_FLAGS(sref) |= SYMBOL_FLAG_LOCAL | SYMBOL_FLAG_LDM;
//   auto reg = gen_reg_rtx(DImode);
//   emit_move_insn(reg, sref);
//   return gen_move_insn(target, gen_rtx_MEM(SImode, reg));
// }

class pass_slave_trace_calls : public rtl_opt_pass {
public:
  pass_slave_trace_calls(gcc::context *ctxt)
      : rtl_opt_pass(pass_data_slave_trace_calls, ctxt) {}
  virtual bool gate(function *f) {
    // return false;
    // if (getenv("COUNT_CALLS") && getenv("COUNT_CALLS")[0] == '1')
    return TARGET_SW_SLAVE; // && getenv("SLAVE_TRACE") &&
                            // getenv("SLAVE_TRACE")[0] == '1';
    // return false;
  }

  virtual unsigned execute(function *f) {
    for (auto insn = get_insns(); insn; insn = NEXT_INSN(insn)) {
      if (GET_CODE(insn) == CALL_INSN) {
        auto pat = PATTERN(insn);
        rtx pat_call = nullptr;
        if (GET_CODE(pat) == PARALLEL) {
          for (int i = 0; i < XVECLEN(pat, 0); i++) {
            auto para_exp = XVECEXP(pat, 0, i);
            if (GET_CODE(para_exp) == SET) {
              para_exp = XEXP(para_exp, 1);
            }
            if (GET_CODE(para_exp) == CALL) {
              pat_call = para_exp;
            }
          }
        }
        rtx symref = nullptr;
        if (pat_call != nullptr) {
          // debug_rtx(XEXP(XEXP(pat_call, 0), 0));
          if (GET_CODE(XEXP(pat_call, 0)) == MEM &&
              GET_CODE(XEXP(XEXP(pat_call, 0), 0)) != SYMBOL_REF) {
            warning_at(INSN_LOCATION(insn), 0,
                       "found non-symbol-ref function call");
            // debug_rtx(XEXP(XEXP(pat_call, 0), 0));
          }
        }
        // if (symref)
        // {

        //   // debug(symref);
        //   if (strcmp(XSTR(symref, 0), "printf") == 0)
        //     continue;
        //   int sym_index = get_index(XSTR(symref, 0));
        //   // printf("%s %d\n", XSTR(symref, 0), get_index(XSTR(symref, 0)));
        //   start_sequence();
        //   auto cntr_sym = gen_rtx_SYMBOL_REF(Pmode,
        //   IDENTIFIER_POINTER(get_identifier("__call_counter")));
        //   SYMBOL_REF_FLAGS(cntr_sym) |= SYMBOL_FLAG_LDM;
        //   SYMBOL_REF_FLAGS(cntr_sym) |= SYMBOL_FLAG_LOCAL;
        //   auto cntr_addr_reg_h = gen_reg_rtx(Pmode);
        //   emit_move_insn(cntr_addr_reg_h, gen_rtx_HIGH(Pmode, cntr_sym));
        //   auto cntr_addr_reg = gen_reg_rtx(Pmode);
        //   emit_move_insn(cntr_addr_reg, gen_rtx_LO_SUM(Pmode,
        //   cntr_addr_reg_h, cntr_sym)); auto cntr_mem = gen_rtx_MEM(SImode,
        //   gen_rtx_PLUS(Pmode, cntr_addr_reg, gen_rtx_CONST_INT(VOIDmode, 4 *
        //   sym_index))); auto cntr_reg = gen_reg_rtx(SImode);
        //   emit_move_insn(cntr_reg, cntr_mem);
        //   auto cntr_reg_p = gen_reg_rtx(SImode);
        //   emit_move_insn(cntr_reg_p, gen_rtx_PLUS(SImode, cntr_reg,
        //   gen_rtx_CONST_INT(VOIDmode, 1))); emit_move_insn(cntr_mem,
        //   cntr_reg_p); auto append = get_insns(); end_sequence();
        //   emit_insn_before(append, insn);
        // }
        // else
        // {
        //   warning(0, "cannot recognize call_insn");
        //   debug(insn);
        // }
      }
    }

    return TODO_df_verify | TODO_df_finish;
  }
};
decltype(targetm) targetm_orig;
void xfort_init_builtins() {
  auto gettid_type = build_function_type_list(intSI_type_node, NULL);
  auto gettid = add_builtin_function("__builtin_gettid", gettid_type, 0x600,
                                     BUILT_IN_MD, "gettid", NULL_TREE);
  //   set_call_expr_flags(gettid, ECF_NOTHROW | ECF_LEAF | ECF_PURE);
  //   set_builtin_decl((built_in_function)0x600, gettid, true);
  targetm_orig.init_builtins();
}
rtx xfort_expand_builtins(tree fn, rtx target, rtx backup, machine_mode mode,
                          int ignored) {
  debug_tree(fn);
  return targetm_orig.expand_builtin(fn, target, backup, mode, ignored);
}
//#include "diagnostic-core.h"
// void add_devices(void *gcc_data, void *user_data) {
//     tree ldmstk = build_decl(BUILTINS_LOCATION, VAR_DECL,
//     get_identifier("ldmstk"), intSI_type_node); TREE_STATIC(ldmstk) = 1;
//     lang_hooks.decls.pushdecl(ldmstk);
//     symbol_attribute sa;
//     gfc_add_intrinsic(&sa, NULL);
//     // varpool_node::add(ldmstk);
// }
void dump_decls(void *gcc_data, void *user_data) {
  // TREE_PUBLIC(ldmstk) = 1;
  varpool_node *node;
  FOR_EACH_DEFINED_VARIABLE(node) { node->dump(stdout); }

  // varpool_node::get_create(ldmstk);
  // puts("!!!");
}
int plugin_is_GPL_compatible = 1;
int plugin_init(struct plugin_name_args *plugin_info,
                struct plugin_gcc_version *version) {
  targetm_orig = targetm;

  // register_callback("xfort", PLUGIN_ATTRIBUTES, initialize_attributes,
  // nullptr); register_callback("xfort", PLUGIN_START_UNIT, add_devices,
  // nullptr);
  // register_callback("xfort", PLUGIN_FINISH_DECL, dump_decls, nullptr);
  // register_pass(new omp2ath_lower_nested(g), PASS_POS_INSERT_BEFORE,
  // "omplower", 1);
  register_pass(new omp2ath(g), PASS_POS_INSERT_BEFORE, "ssa", 1);
  register_pass(new omp2ath_filter(g), PASS_POS_INSERT_BEFORE, "visibility", 1);
  register_pass(new pass_slave_trace_calls(g), PASS_POS_INSERT_BEFORE, "sched1",
                1);
  // targetm.goacc.validate_dims = omp2ath_validate_dims;
  // targetm.goacc.dim_limit = omp2ath_dim_limit;
  // targetm.have_oacc_dim_pos = hook_bool_void_true;
  // targetm.gen_oacc_dim_pos = omp2ath_dim_pos;
  targetm.init_builtins = xfort_init_builtins;
  targetm.expand_builtin = xfort_expand_builtins;
  return 0;
}